//
//  RBHTTPManager.m
//  SaltyHeart
//
//  Created by Lan on 2017/7/18.
//  Copyright © 2017年 LongCai. All rights reserved.
//

#import "RBHTTPManager.h"
#import "AFNetworking.h"
#import "YYModel.h"
#import "RBResponse.h"
#import "RBHTTPRequestTip.h"
#import <YYCategories/NSString+YYAdd.h>
#import <YYCategories/NSDate+YYAdd.h>
#import <YYCache.h>
#import <NSDictionary+YYAdd.h>
#import "RBAccountManager.h"
#import "RBAppDelegateHelper.h"
#import "UIView+RBAdd.h"
#import "RBURLConfig.h"
#import <NSArray+YYAdd.h>
#import "RBEncryption.h"

#define kRBTaskCancelIdentifier @"kRBTaskCancelIdentifier"

@interface RBHTTPManager ()

@property(nonatomic, strong) AFHTTPSessionManager *sessionManager;
@property(nonatomic, strong) NSMutableArray *taskArray;
@property(nonatomic, strong) YYDiskCache *diskCache;

@end

@implementation RBHTTPManager

#pragma mark - Lifecycle

- (instancetype)init
{
    self = [super init];
    if (self)
    {
        self.cancelRequestOnDealloc = YES;
        self.requestTimeoutInterval = 30;
    }
    return self;
}

- (void)dealloc
{
    if (self.cancelRequestOnDealloc)
    {
        [self cancelTasks];
    }
}

#pragma mark - Private

- (BOOL)isConnectedToProxy
{
    if (![RBAccountManager ttc])
    {//规避审核人员挂代理审核不成
        return NO;
    }
    
    NSDictionary *proxySettings = (__bridge NSDictionary *)CFNetworkCopySystemProxySettings();
    NSArray * proxies = (__bridge NSArray *)CFNetworkCopyProxiesForURL((__bridge CFURLRef)[NSURL URLWithString:@"https://www.apple.com"], (__bridge CFDictionaryRef)proxySettings);
    NSDictionary *settings = [proxies objectAtIndex:0];
    if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"])
    {
        //没有设置代理
        return NO;
    }
    else
    {
        [UIApplication.sharedApplication.keyWindow showTip:@"请关闭网络代理后再次尝试" completion:nil];
        //设置代理了
        return YES;
    }
}

#pragma mark - 请求参数处理

/** 规范?key&value中的value值 */
- (NSString *)valueStringFromValue: (id)value
{
    if ([value isKindOfClass:[NSString class]])
    {
        return (NSString *)value;
    }
    else if ([value isKindOfClass:[NSDictionary class]])
    {
        NSDictionary *dic = (NSDictionary *)value;
        return dic.jsonStringEncoded;
    }
    else if ([value isKindOfClass:[NSArray class]])
    {
        NSArray *array = (NSArray *)value;
        return array.jsonStringEncoded;
    }
    else if ([value isKindOfClass:[NSNumber class]])
    {
        NSNumber *num = (NSNumber *)value;
        return num.stringValue;
    }
    else if([value isKindOfClass:[NSObject class]])
    {
        return [value yy_modelToJSONString];
    }
    else
    {
        return @"";
    }
}

/** 模型/字典统一转换成字典,规范化value值 */
- (NSDictionary *)dicFromParameters: (id)parameters
{
    NSDictionary *dic = nil;
    if ([parameters isKindOfClass:[NSDictionary class]])
    {
        dic = (NSDictionary *)parameters;
    }
    else if([parameters isKindOfClass:[NSObject class]])
    {
        dic = [parameters yy_modelToJSONObject];
    }
    
    if (![dic isKindOfClass:[NSDictionary class]])
    {
        return nil;
    }
    
    NSArray *keys = dic.allKeys;
    NSMutableDictionary *dicM = [NSMutableDictionary dictionary];
    for (NSString *key in keys)
    {
        dicM[key] = [self valueStringFromValue:dic[key]];
    }
    return dicM;
}

/** 字典转字符串,key排序,url encode */
- (NSString *)stringFromDictionary: (NSDictionary *)dictionary
{
    NSMutableArray *arrayM = [NSMutableArray array];
    NSArray *keys = dictionary.allKeys;
    keys = [keys sortedArrayUsingComparator:^NSComparisonResult(NSString *key1, NSString *key2) {
        return [key1 compare:key2];
    }];
    for (NSString *key in keys)
    {
        NSString *value = dictionary[key];
        [arrayM addObject:[NSString stringWithFormat:@"%@=%@", key, [value stringByURLEncode]]];
    }
    return [arrayM componentsJoinedByString:@"&"];
}

/** 字符串转NSData */
- (NSData *)dataFromString: (NSString *)string
{
    return [string dataUsingEncoding:NSUTF8StringEncoding];
}

/** 处理请求参数,格式化,自增参数 */
- (NSDictionary *)handleRequestWithPath: (NSString *)path parameters: (id)parameters
{
    NSDictionary *dic = [self dicFromParameters:parameters];
    dic = dic? dic: [NSDictionary dictionary];
    NSMutableDictionary *dicM = [NSMutableDictionary dictionaryWithDictionary:dic];
    //统一添加请求参数
//    dicM[@"client"] = @"iOS";
    
    if([RBAccountManager getUserId].length)
    {
        dicM[@"user_id"] = [RBAccountManager getUserId];
    }
    
    //先打印
    [self requestLogWithPath:path param:dicM];
    //再加密处理
    if ([self securityTransport])
    {
        NSMutableDictionary *securityDicM = [NSMutableDictionary dictionary];
        NSString *jsonString = dicM.jsonStringEncoded;
        NSString *securityString = [self encodeWithString:jsonString];
        securityDicM[@"params"] = securityString;
        return securityDicM;
    }
    else
    {
        return dicM;
    }
}

- (NSDictionary *)headers
{
    NSMutableDictionary *dicM = [NSMutableDictionary dictionary];
    if ([[RBAccountManager getToken] length] > 0) {
        dicM[@"token"] = [RBAccountManager getToken];
    }
    return dicM;
}

#pragma mark - 响应数据处理

- (id)responseObjectFromResponseData: (NSData *)responseData
{
    if ([responseData isKindOfClass:[NSData class]] && responseData.length)
    {
        NSError *jsonError = nil;
        
        id responseObject = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonError];
        if (jsonError)
        {
            responseObject = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
        }
        return responseObject;
    }
    else
    {
        return nil;
    }
}

- (id)objectWithResponse: (NSHTTPURLResponse *)response
          responseObject: (NSDictionary *)responseObject
                   error: (NSError *)error
       responseDataClass:(Class)responseClass
{
    NSObject *object;
    if (responseClass)
    {
        object = [responseClass yy_modelWithJSON:responseObject];
        if (!object)
        {
            object = [responseClass new];
        }
        if ([object isKindOfClass:[RBResponse class]])
        {
            RBResponse *res = (RBResponse *)object;
            res.headers = [response isKindOfClass:[NSHTTPURLResponse class]]? response.allHeaderFields: nil;
            res.error = error;
            res.responseObject = responseObject;
            if (!res.message)
            {
                if (error)
                {
                    switch (error.code)
                    {
                        case -1022:
                            res.message = RB_HTTP_REQUEST_PROJECT_CONFIG_TIP;
                            break;
                        case -1001:
                            res.message = RB_HTTP_REQUEST_TIMEOUT_TIP;
                            break;
                        case -1009:
                            res.message = RB_HTTP_NET_CONNECT_ERROR_TIP;
                            break;
                        default:
                            res.message = RB_HTTP_REQUEST_FAIL_TIP;
                            break;
                    }
                }
                else
                {
                    res.message = RB_HTTP_REQUEST_FAIL_TIP;
                }
            }
        }
    }
    else
    {
        object = responseObject;
    }
    return object;
}

- (id)handleResponse: (NSHTTPURLResponse *)response
        responseObject: (id)responseObject
               error: (NSError *)error
   responseDataClass:(Class)responseClass
{
    //转Dic
    NSDictionary *dic = nil;
    if ([responseObject isKindOfClass:[NSDictionary class]])
    {
        dic = (NSDictionary *)responseObject;
    }
    else if ([responseObject isKindOfClass:[NSString class]] && [self securityTransport])
    {
        dic = [[self decodeWithString:(NSString *)responseObject] jsonValueDecoded];
    }
    //log
    [self responseLogWithPath:[response isKindOfClass:[NSHTTPURLResponse class]]? response.URL.absoluteString: nil data:dic.allKeys.count? dic: responseObject];
    //转model
    id object = [self objectWithResponse:response responseObject:dic error:error responseDataClass:responseClass];
    /*
    //检测登录过期
    if ([object isKindOfClass:[RBResponse class]])
    {
        NSInteger code = ((RBResponse *)object).code;
        if (code == 888)
        {
            [self logout];
        }
    }
     */
    return object;
}

#pragma mark - 缓存处理

- (NSString *)cacheIdentifierWithPath: (NSString *)path parameters: (NSDictionary *)dic
{
    return [[NSString stringWithFormat:@"%@-%@", path, [self stringFromDictionary:dic]] sha1String];
}

- (NSString *)taskIdentifier
{
    return [[[NSDate date] stringWithFormat:@"yyyyMMddHHmmssSS"] sha1String];
}

#pragma mark - 加密处理

- (BOOL)securityTransport
{
    return kRBSecurityTransport && !self.ignoreSecurityTransport;
}

- (NSString *)encodeWithString: (NSString *)string
{
    return [RBEncryption aesEncrypt:string];
}

- (NSString *)decodeWithString: (NSString *)string
{
    return [RBEncryption aesDecrypt:string];
}

#pragma mark - GET/POST请求

- (NSString *)dataTaskWithMethod: (NSString *)method
                         URLPath: (NSString *)path
                      parameters: (id)parameters
               responseDataClass: (Class)responseClass
                   cancelRequest: (BOOL)cancelRequest
                  cacheGetPolicy: (RBHTTPManagerCacheGetPolicy)cacheGetPolicy
                   cacheGetBlock: (void(^)(id responseObjecta))cacheGetBlock
                 completionBlock: (BOOL(^)(NSHTTPURLResponse *response, id responseObject, NSError * error))completionBlock
{
    if (cancelRequest)
    {
        [self cancelTasks];
    }
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:path]];
    request.HTTPMethod = method;
    request.timeoutInterval = self.requestTimeoutInterval;
    
    NSDictionary *processedDic = [self handleRequestWithPath:path parameters:parameters];
    if ([method isEqualToString:@"POST"])
    {
        request.HTTPBody = processedDic? [self dataFromString:[self stringFromDictionary:processedDic]]: nil;
    }
    
    //添加header(这里需要这么添加)
    NSDictionary *headers = [self headers];
    for (NSString *key in headers.allKeys)
    {
        [request setValue:headers[key] forHTTPHeaderField:key];
    }
    
    __weak typeof(self) weak_self = self;
    NSString *cacheIdentifier = [self cacheIdentifierWithPath:path parameters:processedDic];
    //只要缓存
    if (cacheGetPolicy == RBHTTPManagerCacheGetPolicyCacheOnly)
    {
        id cacheObject = [self.diskCache objectForKey:cacheIdentifier];
        cacheGetBlock([weak_self handleResponse:nil responseObject:cacheObject error:nil responseDataClass:responseClass]);
        return nil;
    }
    //首要缓存
    else if (cacheGetPolicy == RBHTTPManagerCacheGetPolicyCacheElseRequest && [self.diskCache containsObjectForKey:cacheIdentifier])
    {
        id cacheObject = [self.diskCache objectForKey:cacheIdentifier];
        cacheGetBlock([weak_self handleResponse:nil responseObject:cacheObject error:nil responseDataClass:responseClass]);
        return nil;
    }
    //首先缓存
    else if (cacheGetPolicy == RBHTTPManagerCacheGetPolicyCacheBeforeRequest && [self.diskCache containsObjectForKey:cacheIdentifier])
    {
        id cacheObject = [self.diskCache objectForKey:cacheIdentifier];
        cacheGetBlock([weak_self handleResponse:nil responseObject:cacheObject error:nil responseDataClass:responseClass]);
    }
    
    __block NSURLSessionDataTask *task = [self.sessionManager dataTaskWithRequest:request uploadProgress:^(NSProgress * _Nonnull uploadProgress){
          
      } downloadProgress:^(NSProgress * _Nonnull downloadProgress){
          
      } completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseData, NSError * _Nullable error){
          //容错
          if (!weak_self)
          {
              return;
          }
          
          [weak_self.taskArray removeObject:task];
          //如果请求取消了,忽略,不给回调
          if ([task.taskDescription isEqualToString:kRBTaskCancelIdentifier])
          {
              NSLog(@"主动取消了一次请求");
              return;
          }
          
          NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
          id responseObject = [self responseObjectFromResponseData:responseData];
          id netObject = [self handleResponse:httpResponse responseObject:responseObject error:error responseDataClass:responseClass];
          
          //返回数据有问题 && 尝试返回缓存 && 有缓存
          if (error && cacheGetPolicy == RBHTTPManagerCacheGetPolicyCacheAfterRequest && [weak_self.diskCache containsObjectForKey:cacheIdentifier])
          {
              id cacheObject = [weak_self.diskCache objectForKey:cacheIdentifier];
              cacheGetBlock([weak_self handleResponse:nil responseObject:cacheObject error:nil responseDataClass:responseClass]);
          }
          //数据没问题 || 不能返回缓存
          else
          {
              BOOL needCache = completionBlock((NSHTTPURLResponse *)response,netObject,error);
              if (needCache)
              {
                  [weak_self.diskCache setObject:responseObject forKey:cacheIdentifier];
              }
          }
      }];
    [self.taskArray addObject:task];
    [task resume];
    task.taskDescription = [self taskIdentifier];
    return task.taskDescription;
}

#pragma mark - 应用登出

- (void)logout
{
    /*
    [RBAccountManager setSessionId:nil];
    RBAppDelegateHelper.keyWindow.rootViewController = RBAppDelegateHelper.loginVC;
    [RBAppDelegateHelper.keyWindow.rootViewController.view showTip:@"登录过期,请重新登录" completion:nil];
     */
}

#pragma mark - Log

- (void)requestLogWithPath: (NSString *)path param: (NSDictionary *)param
{
#ifndef DEBUG
    return;
#endif
    if (!kRBHTTPManagerLog)
    {
        return;
    }
    NSString *begin = @"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓";
    NSString *end = @"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑";
    NSMutableString *strinM = [NSMutableString string];
    [strinM appendFormat:@"\n%@请求开始%@", begin, begin];
    [strinM appendFormat:@"\n===>path\n%@", path];
    for (NSString *key in param.allKeys)
    {
        [strinM appendFormat:@"\n===>%@", key];
        [strinM appendFormat:@"\n%@", param[key]];
    }
    [strinM appendFormat:@"\n%@请求结束%@\n", end, end];
    NSLog(@"%@", strinM);
}

- (void)responseLogWithPath: (NSString *)path data: (id)data
{
#ifndef DEBUG
    return;
#endif
    if (!kRBHTTPManagerLog)
    {
        return;
    }
    NSString *begin = @"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓";
    NSString *end = @"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑";
    NSMutableString *strinM = [NSMutableString string];
    [strinM appendFormat:@"\n%@响应开始%@", begin, begin];
    [strinM appendFormat:@"\n===>path\n%@", path];
    NSString *content = nil;
    if ([data isKindOfClass:[NSDictionary class]])
    {
        content = ((NSDictionary *)data).jsonPrettyStringEncoded;
    }
    else if ([data isKindOfClass:[NSString class]])
    {
        content = (NSString *)data;
    }
    [strinM appendFormat:@"\n===>response\n%@", content];
    [strinM appendFormat:@"\n===>token\n%@", [RBAccountManager getToken]];
    [strinM appendFormat:@"\n%@响应结束%@\n", end, end];
    NSLog(@"%@", strinM);
}

#pragma mark - Public

- (BOOL)loading
{
    return self.taskArray.count;
}

- (void)cancelTasks
{
    for (NSURLSessionDataTask *task in self.taskArray)
    {
        task.taskDescription = kRBTaskCancelIdentifier;
        [task cancel];
    }
    [self.taskArray removeAllObjects];
}

- (void)cancelTaskWithIdentifier: (NSString *)taskIdentifier
{
    for (NSInteger i = 0; i < self.taskArray.count; i++)
    {
        NSURLSessionDataTask *task = self.taskArray[i];
        if ([task.taskDescription isEqualToString:taskIdentifier])
        {
            [task cancel];
            [self.taskArray removeObject:task];
        }
    }
}

- (NSString *)postWithURLPath: (NSString *)path
                   parameters: (id)parameters
            responseDataClass: (Class)responseClass
                cancelRequest: (BOOL)cancelRequest
               cacheGetPolicy: (RBHTTPManagerCacheGetPolicy)cacheGetPolicy
                cacheGetBlock: (void(^)(id responseObjecta))cacheGetBlock
              completionBlock: (BOOL(^)(NSHTTPURLResponse *response, id responseObject, NSError * error))completionBlock;
{
    return [self dataTaskWithMethod:@"POST" URLPath:path parameters:parameters responseDataClass:responseClass cancelRequest:cancelRequest cacheGetPolicy:cacheGetPolicy cacheGetBlock:cacheGetBlock completionBlock:completionBlock];
}

- (NSString *)getWithURLPath: (NSString *)path
                  parameters: (id)parameters
           responseDataClass: (Class)responseClass
               cancelRequest: (BOOL)cancelRequest
              cacheGetPolicy: (RBHTTPManagerCacheGetPolicy)cacheGetPolicy
               cacheGetBlock: (void(^)(id responseObjecta))cacheGetBlock
             completionBlock: (BOOL(^)(NSHTTPURLResponse *response, id responseObject, NSError * error))completionBlock
{
    return [self dataTaskWithMethod:@"GET" URLPath:parameters? [NSString stringWithFormat:@"%@?%@", path, [self stringFromDictionary:[self dicFromParameters:parameters]]]: path parameters:parameters responseDataClass:responseClass cancelRequest:cancelRequest cacheGetPolicy:cacheGetPolicy cacheGetBlock:cacheGetBlock completionBlock:completionBlock];
}

- (NSString *)postWithPath: (NSString *)path
                parameters:(id)parameters
                  fileData:(NSArray *)dataArray
                      name:(NSArray *)nameArray
                  fileName:(NSArray *)fileNameArray
                  mimeType:(NSArray *)mimeTypeArray
         responseDataClass: (Class)responseClass
             cancelRequest: (BOOL)cancelRequest
                  progress: (void(^)(NSProgress * uploadProgress))progressBlock
                completion: (void(^)(NSHTTPURLResponse *response, id responseObject, NSError * error))completionBlock
{
    if (cancelRequest)
    {
        [self cancelTasks];
    }
    __weak typeof(self) weak_self = self;
    NSDictionary *processedDic = [self handleRequestWithPath:path parameters:parameters];
    NSURLSessionDataTask *task = [self.sessionManager POST:path parameters:processedDic headers:[self headers] constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData)
    {
        for (NSInteger i = 0; i < dataArray.count; i++)
        {
            NSData *data = dataArray[i];
            NSString *name = nameArray[i];
            NSString *fileName = fileNameArray[i];
            NSString *mimeType = mimeTypeArray[i];
            [formData appendPartWithFileData:data name:name fileName:fileName mimeType:mimeType];
        }
    } progress:^(NSProgress * _Nonnull uploadProgress)
    {
        if (!weak_self) return;
        if (progressBlock)
        {
            progressBlock(uploadProgress);
        }
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseData)
    {
        if (!weak_self) return;
        [weak_self.taskArray removeObject:task];
        id responseObject = [self responseObjectFromResponseData:responseData];
        completionBlock(nil, [weak_self handleResponse:(NSHTTPURLResponse *)task.response responseObject:responseObject error:nil responseDataClass:responseClass], nil);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
    {
        if (!weak_self) return;
        [weak_self.taskArray removeObject:task];
        completionBlock(nil, nil, error);
    }];
    [self.taskArray addObject:task];
    [task resume];
    task.taskDescription = [self taskIdentifier];
    return task.taskDescription;
}

#pragma mark - Getter

- (AFHTTPSessionManager *)sessionManager
{
    if (!_sessionManager)
    {
        self.sessionManager = ({
            AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager];
//            sessionManager.responseSerializer.acceptableContentTypes = ({
//                NSMutableSet *setM = [NSMutableSet setWithSet:sessionManager.responseSerializer.acceptableContentTypes];
//                [setM addObject:@"text/html"];
//                [setM addObject:@"text/plain"];
//                setM;
//            });
            sessionManager.responseSerializer = [AFHTTPResponseSerializer new];
            sessionManager;
        });
    }
    return _sessionManager;
}

- (NSMutableArray *)taskArray
{
    if (!_taskArray)
    {
        self.taskArray = [NSMutableArray array];
    }
    return _taskArray;
}

- (YYDiskCache *)diskCache
{
    if (!_diskCache)
    {
        NSString *cacheFolder = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
        NSString *path = [cacheFolder stringByAppendingPathComponent:@"networkCache"];
        self.diskCache = [[YYDiskCache alloc] initWithPath:path];
    }
    return _diskCache;
}

@end
